#pragma once
#include "mpegparser/M2VParser.h"
#include <comdef.h>
#include <list>
#include <vector>

namespace MkvMux
{

class CStreamIO
{
    CStreamIO(const CStreamIO&);
    CStreamIO& operator=(const CStreamIO&);
    
public:

    //TODO: handle something besides MPEG-2
    CStreamIO(IStream*, const BYTE*, ULONG);

    ~CStreamIO();
    
    HRESULT WriteVideo(IMediaSample*);    
    
private:

    void Init();   //transition from Stopped
    void Final();  //transition to Stopped

private:

    const IStreamPtr m_pStream;
    //const MPEG2SequenceHeader m_hdr;  //TODO
    
    //typedef std::vector<BYTE> blob_t;
    const std::vector<BYTE> m_hdr;

    M2VParser m_parser;  //TODO: use decorator pattern
                         //for now, just hard-code mpg2
    
    void WriteFrame(MPEGFrame*);
    //void WriteEOS();
    
    static __int64 SetPosition(IStream*, __int64, STREAM_SEEK);
    __int64 SetPosition(__int64, STREAM_SEEK = STREAM_SEEK_SET) const;

    __int64 GetPosition() const;
    
    static void Serialize(ISequentialStream*, const BYTE*, const BYTE*);
    
    static void Serialize(ISequentialStream*, const void*, ULONG);
    void Serialize(const void*, ULONG);    

    static void Write(ISequentialStream*, const void*, ULONG);
    void Write(const void*, ULONG);
    
    static void SetSize(IStream*, __int64);
    void SetSize(__int64);
    
    static void WriteID4(ISequentialStream*, ULONG);
    void WriteID4(ULONG);

    static void WriteID3(ISequentialStream*, ULONG);
    void WriteID3(ULONG);

    static void WriteID2(ISequentialStream*, USHORT);
    void WriteID2(USHORT);

    static void WriteID1(ISequentialStream*, BYTE);
    void WriteID1(BYTE);

    static void Write8UInt(ISequentialStream*, __int64);
    void Write8UInt(__int64);
    
    static void Write4UInt(ISequentialStream*, ULONG);
    void Write4UInt(ULONG);
    
    static void Write2UInt(ISequentialStream*, USHORT);
    void Write2UInt(USHORT);

    static void Write1UInt(ISequentialStream*, BYTE);
    void Write1UInt(BYTE);
    
    static void Write2SInt(ISequentialStream*, SHORT);
    void Write2SInt(SHORT);

    static void WriteData4Binary(ISequentialStream*, const void*, ULONG);
    void WriteData4Binary(const void*, ULONG);
    
    static void WriteData4String(ISequentialStream*, const char*);
    void WriteData4String(const char*);
    
    static void WriteData4UTF8(ISequentialStream*, const wchar_t*);
    void WriteData4UTF8(const wchar_t*);

    static void WriteData4UInt(ISequentialStream*, ULONG);
    void WriteData4UInt(ULONG);

    static void WriteData4Float(ISequentialStream*, float);
    void WriteData4Float(float);
    
    static void WriteData4Void(IStream*, ULONG);
    void WriteData4Void(ULONG);
    
    void WriteEbmlHeader();
    
    void InitSegment();
    void FinalSegment();    

    void InitSeekHead();
    void FinalSeekHead();
    void WriteSeekEntry(ULONG, __int64);

    void InitInfo();
    void FinalInfo();

    void WriteTrack();
    
    void FinalClusters(__int64);
    
    void WriteCues();
    
    struct Cluster
    {
        __int64 m_pos;
        LONG m_timecode_ms;  //of this cluster
        
        void WriteCuePoints(CStreamIO&) const;                
        void Final(CStreamIO&, ULONG&, const Cluster&);
        
        struct Keyframe
        {
            LONG m_timecode_ms;
            LONG m_block_number;
            
            Keyframe() {}
            
            Keyframe(LONG t, LONG n) :
                m_timecode_ms(t),
                m_block_number(n)
            {
            }
            
            void WriteCuePoint(CStreamIO&, const Cluster&) const;
        };
        
        typedef std::list<Keyframe> keyframes_t;
        keyframes_t m_keyframes;
    };
    
    //TODO: does MS have a bloody slist yet?
    typedef std::list<Cluster> clusters_t;
    clusters_t m_clusters;
    
    //This is a running count of the frames in the
    //most recent cluster created.  It's needed 
    //to mark the block number, which is used later
    //when creating the cues for the cluster.
    LONG m_cFrames;
    
    void NewCluster(MPEGFrame*);    
    
    __int64 m_segment_pos;
    __int64 m_seekhead_pos;    
    __int64 m_info_pos;
    __int64 m_track_pos;
    __int64 m_cues_pos;

    __int64 m_duration_pos;    
    LONG m_max_timecode_ms;

    typedef unsigned __int64 TrackUID_t;    
    static TrackUID_t CreateTrackUID();
    
public:

    __int64 GetDuration() const;  //reftime units

};


}  //end namespace MkvMux
